package com.cj.framework.config;

import com.cj.framework.entity.Hr;
import com.cj.framework.entity.RespBean;
import com.cj.framework.service.IHrService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.PrintWriter;

@Configuration
public class SecurityConfig {

    /**
     * 登录之后的用户信息： 默认存两个地方：1.HttpSession  2.SecurityContextHolder
     *    在登录请求之后，SecurityContextHolder中的用户信息会被清除掉，，，，为什么后续的请求还是有用户信息，，是因为后续请求，spring security都会从HttpSession获取用户信息
     *
     * 新版的spring security ，，自己配置的 UsernamePasswordAuthenticationFilter，，需要指定 用户信息存放位置，，是要存到redis中，还是存到其他的地方
     *
     *
     * 配置了 UsernamePasswordAuthenticationFilter ，， 之后， 之前配置的登录页面的信息就失效了
     * @return
     */
    @Bean
    JsonFilter jsonFilter(){
        JsonFilter jsonFilter = new JsonFilter();
        jsonFilter.setFilterProcessesUrl("/login");
        jsonFilter.setAuthenticationSuccessHandler((req,resp,auth)->{
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter writer = resp.getWriter();
            Hr hr = (Hr) auth.getPrincipal();
            hr.setPassword(null);
            writer.write(new ObjectMapper().writeValueAsString(RespBean.ok("登录成功",hr)));
        });
        jsonFilter.setAuthenticationFailureHandler((req,resp,e)->{
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter writer = resp.getWriter();

            RespBean error = RespBean.error("登录失败");

            if (e instanceof BadCredentialsException){
                error.setMessage("用户名或者密码输入错误");
            }else if (e instanceof DisabledException){
                error.setMessage("账户被禁用");
            }else if(e instanceof LockedException){
                error.setMessage("账户被锁定");
            }else if (e instanceof AccountExpiredException){
                error.setMessage("账户过期");
            }else if (e instanceof CredentialsExpiredException){
                error.setMessage("密码过期");
            }

            writer.write(new ObjectMapper().writeValueAsString(error));
        });

        // 认证管理器   设置自己配置的 AuthenticationManager
        jsonFilter.setAuthenticationManager(authenticationManager());

        // 设置将 用户信息，存放在哪里，，，， 新版的spring security 必须指定，，用户信息存放位置
        jsonFilter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());

        return jsonFilter;
    }
    

    @Autowired
    IHrService hrService;

    @Bean
    AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        // 需要设置一个 UserDetailService ，，因为需要调用这个 的验证方法获取数据库用户名密码
        daoAuthenticationProvider.setUserDetailsService(hrService);
        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        return providerManager;
    }

    // 过滤器链
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 拦截所有请求，但是不经过任何过滤器链
//        return new DefaultSecurityFilterChain(new AntPathRequestMatcher("/**"));

        // build方法返回的就是 spring security 默认过滤器基础之上过滤
//        http.build()

        /**
         * spring security 默认 key-value 传参数，，，
         *
         * 配置过滤器链
         */

        // 配置拦截规则
         http.authorizeHttpRequests(p -> p.anyRequest().authenticated())
                .formLogin(f -> f.usernameParameter("username").passwordParameter("password")
                        // 登录地址
                        .loginProcessingUrl("/login")
                        .successHandler((req, resp, auth) -> {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter writer = resp.getWriter();
                            Hr hr = (Hr) auth.getPrincipal();
                            hr.setPassword(null);
                            writer.write(new ObjectMapper().writeValueAsString(RespBean.ok("登录成功", hr)));
                        }).failureHandler((req, resp, e) -> {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter writer = resp.getWriter();

                            RespBean error = RespBean.error("登录失败");

                            if (e instanceof BadCredentialsException) {
                                error.setMessage("用户名或者密码输入错误");
                            } else if (e instanceof DisabledException) {
                                error.setMessage("账户被禁用");
                            } else if (e instanceof LockedException) {
                                error.setMessage("账户被锁定");
                            } else if (e instanceof AccountExpiredException) {
                                error.setMessage("账户过期");
                            } else if (e instanceof CredentialsExpiredException) {
                                error.setMessage("密码过期");
                            }

                            writer.write(new ObjectMapper().writeValueAsString(error));


                        }))
                .csrf(c -> c.disable())

                .exceptionHandling(e -> e.authenticationEntryPoint((req, resp, ex) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    resp.setStatus(401);
                    RespBean error = RespBean.error("尚未登录，请登录");
                    resp.getWriter().write(new ObjectMapper().writeValueAsString(error));
                }));

        // 添加过滤器，，，添加在某个之前
        http.addFilterBefore(jsonFilter(), UsernamePasswordAuthenticationFilter.class);
        return   http.build();
    }
}
